#    Copyright 2008, 2009 Tri-County Electric Cooperative, St. Matthews, South Carolina
#    This file is part of COOPGIS.
#    COOPGIS is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#    COOPGIS is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#    You should have received a copy of the GNU General Public License
#    along with COOPGIS.  If not, see <http://www.gnu.org/licenses/>.

import os, pickle
import datetime
FTPFileName='/opt/coopgis/fromgps/anonymousftproot_OtherReadOnly/incomingftpallpermissions/Locations.csv'
import shutil

#************************BEGIN Add Include Files to Path************************
import sys
NeededPaths=['/opt/coopgis/djangoproject/include', '/opt/coopgis/djangoproject']
for path in NeededPaths:
    if(not path in sys.path):
        sys.path.append(path)
#************************END   Add Include Files to Path************************
from django.contrib.gis.geos import *
import os, time, psycopg2, datetime
#********************Setup psycopg2 and django section********************
PathDjangoRoot="/opt/coopgis/"
if(not PathDjangoRoot in sys.path):
    sys.path.append(PathDjangoRoot)
import djangoproject
from djangoproject import settings
from models import *
#********************Setup psycopg2 and django section********************

from django.template import Context, loader
from django.http import HttpResponse
from django import forms
import os
import datetime
import djangoproject.settings

import COOPGIS_Update

def GetUserNameOrIPAddress(request, GetDefaultMap=False, ApplicationName=None):
    if(bool(request.user.username)):
        UserName=str(request.user.username)
    else:
        UserName=str(request.META['REMOTE_ADDR']) 
    if(GetDefaultMap):
        try:
            DefaultMap=ApplicationDefaultMap.objects.filter(ApplicationName=ApplicationName, UserName=UserName)[0].Map
        except:
            try:
                DefaultMap=ApplicationDefaultMap.objects.filter(ApplicationName=ApplicationName, UserName='')[0].Map
            except:
                DefaultMap=Map.objects.all()[0]
        return UserName, DefaultMap
    else:
        return UserName

class DynForm(forms.Form):    
    """Dynamic form that allows the user to change and then verify the data that was parsed"""
    def setFields(self, kwds):
        """Set the fields in the form"""
        keys = kwds.keys()
        keys.sort()
        for k in keys:
            self.fields[k] = kwds[k]            
    def setData(self, kwds):
        """Set the data to include in the form"""
        self.is_bound = True 
        keys = kwds.keys()
        keys.sort()
        for k in keys:
            self.data[k] = kwds[k]
    def validate(self, post):
        """Validate the contents of the form"""
        for name,field in self.fields.items():
            try:
                field.clean(post[name])
            except ValidationError, e:
                self.errors[name] = e.messages
    class Media:  #Should not affect any non-Map forms
        js=("http://localhost/openlayers/OpenLayers.js",)

def UpdateSelection(station, span, UserNameOrIPAddress):  
    """Updates the highlighted Station and Span by Username"""
    SelectionPoint.objects.filter(UserName=UserNameOrIPAddress).delete()
    if(station):
        New=SelectionPoint()
        New.Point=station.Point
        New.UserName=UserNameOrIPAddress
        New.save()
    SelectionLine.objects.filter(UserName=UserNameOrIPAddress).delete()
    if(span):
        New=SelectionLine()
        New.Line=span.Line
        New.UserName=UserNameOrIPAddress
        New.save()

def GetNearbyInfo(station, span):
    StationAssemblyList = ['None']
    SpanAssemblyList = ['None']
    if(station):
	StationAssemblyList=station.GetNearbyStationInfo()
	SpanAssemblyList=station.GetNearbySpanInfo()
    elif(span):
	PointCoordinates=span.Line[0]
	point=Point(x=PointCoordinates[0], y=PointCoordinates[1])
	point.srid=span.Line.srid
	InfoStation=Station.objects.filter(Point__distance_lte=(point, .25 * djangoproject.settings.COOPGISDistanceMultiplier))[0]  #0.25 feet
        StationAssemblyList, SpanAssemblyList=GetNearbyInfo(InfoStation, span)
    return StationAssemblyList, SpanAssemblyList



def GenerateDynamicMapFile(ApplicationName, UserName, stakerWorkOrder='', MapReportIdentifier=''):
    os.system("sudo -u apachescript sudo chmod -R 777 /opt/coopgis/mapserver/")
    try:
        MapFile=ApplicationDefaultMap.objects.filter(ApplicationName=ApplicationName, UserName=UserName)[0].Map
    except:
        try:
            MapFile=ApplicationDefaultMap.objects.filter(ApplicationName=ApplicationName, UserName='')[0].Map
        except:
            MapFile=Map.objects.all().order_by('id')[0]
    MapFileName=MapFile.GenerateMapFile(UserName, ApplicationName, stakerWorkOrder=stakerWorkOrder, 
                                        MapReportIdentifier=MapReportIdentifier)
    LayerName=MapFile.GetLayerName()
    return MapFileName, LayerName



def CheckLoginPermissions(request, ApplicationName):
    if request.user.is_anonymous():
        LoginStatus="You are not currently logged in.  Please login through the Main Menu."
        applicationPermission=False
    else:
        try:
            applicationPermission=bool(ApplicationName in str(list(request.user.groups.all())))
        except:
            applicationPermission=False
        if(applicationPermission):
            LoginStatus="Welcome to COOPGIS " + str(ApplicationName) + ', '  + request.user.username + '.'
        else:
            LoginStatus="Your account does not have permission to access " + str(ApplicationName) + ', '  + request.user.username + '.' 


    if(request.user.username.lower() in djangoproject.settings.COOPGISUsersWithDelayAfterUpdating):
        LastUpdateTime=COOPGIS_Update.GetLastUpdateDateTime()
        TimeSinceLastUpdate=datetime.datetime.now() - LastUpdateTime
        SecondsSinceLastUpdate=TimeSinceLastUpdate.seconds
        TimeBetweenUpdates = 60 * 10 #seconds * minutes
        if(TimeSinceLastUpdate.days == 0) and (TimeSinceLastUpdate.seconds < TimeBetweenUpdates): 
            SecondsLeft = TimeBetweenUpdates - TimeSinceLastUpdate.seconds
            raise(Exception('You must wait:  ' + str(TimeBetweenUpdates) + ' seconds after updating before using COOPGIS.\
                             Time left until you can update again:  ' + str(SecondsLeft) + ' seconds.\n'))
            applicationPermission=False

    return LoginStatus, applicationPermission


def GetOneOrNone(QuerySet):
    try:
        return QuerySet[0]
    except:
        return None

def ConvertPointsToPolygon(Points):
    Points.append(Points[0])
    try:
        ring=LinearRing(Points)
        poly=Polygon(ring)
        return poly
    except:
        print str(OrderedPoints) + '   Failed....'
        return None

def GetBoundingPolygon(StartPoint, EndPoint, BoundingDistance):
    #x1, y1 = lower values
    #x2, y2 = higher values
    if(StartPoint.x < EndPoint.x):
        x1 = StartPoint.x - BoundingDistance
        x2 = EndPoint.x + BoundingDistance
    else:
        x1 = EndPoint.x - BoundingDistance
        x2 = StartPoint.x + BoundingDistance
    if(StartPoint.y < EndPoint.y):
        y1 = StartPoint.y - BoundingDistance
        y2 = EndPoint.y + BoundingDistance
    else:
        y1 = EndPoint.y - BoundingDistance
        y2 = StartPoint.y + BoundingDistance
    Poly = ConvertPointsToPolygon([Point(x1, y1, srid=StartPoint.srid),
                                   Point(x1, y2, srid=StartPoint.srid),
                                   Point(x2, y2, srid=StartPoint.srid),
                                   Point(x2, y1, srid=StartPoint.srid)])
    return Poly

def GetBoundingPolygonSinglePoint(StartPoint, XBoundingDistance, YBoundingDistance):
    x1 = StartPoint.x 
    x2 = StartPoint.x + XBoundingDistance
    y1 = StartPoint.y 
    y2 = StartPoint.y + YBoundingDistance
    Poly = ConvertPointsToPolygon([Point(x1, y1, srid=StartPoint.srid),
                                   Point(x1, y2, srid=StartPoint.srid),
                                   Point(x2, y2, srid=StartPoint.srid),
                                   Point(x2, y1, srid=StartPoint.srid)])
    return Poly



def GetAssembliesWithinPolygon(polygon):
    if(not polygon):
        return []
    Stations=Station.objects.filter(Point__within=polygon).order_by('id')
    Spans=Span.objects.filter(Line__within=polygon).order_by('id')
    Assemblies=[]
    for station in Stations:
        tempAssemblies=Assembly.objects.filter(Station=station)
        for assembly in tempAssemblies:
            Assemblies.append(assembly)
        if(len(Assemblies) > 9000):  #Limit so page load completes in a reasonable time even if a very large polygon is selected
            return Assemblies
    for span in Spans:
        tempAssemblies=Assembly.objects.filter(Span=span)
        for assembly in tempAssemblies:
            Assemblies.append(assembly)
        if(len(Assemblies) > 9000):
            return Assemblies
    return Assemblies
    
def CalculateFarPointQuadrant(OriginPoint, FarPoint):
    if(FarPoint.x >= OriginPoint.x and FarPoint.y >= OriginPoint.y):
        Quadrant=1
    elif(FarPoint.x < OriginPoint.x and FarPoint.y >= OriginPoint.y):
        Quadrant=2
    elif(FarPoint.x < OriginPoint.x and FarPoint.y <= OriginPoint.y):
        Quadrant=3
    elif(FarPoint.x > OriginPoint.x and FarPoint.y <= OriginPoint.y):
        Quadrant=4
    else:
        raise(Exception('Unable to determine quadrant'))
    return Quadrant

def CalculateAngle(OriginPoint, FarPoint1, FarPoint2): 
    """Calculates the angle between an extension of FarPoint1 through the origin with the line formed by OriginPoint and FarPoint2
       uses law of cosines:  c^2 = a^2 + b^2 - 2abcos(gamma)
       solved for gamma:
       arccos((c^2-a^2-b^2)/(-2ab))=gamma"""
    ExtendedPointX=OriginPoint.x + (OriginPoint.x - FarPoint1.x)
    ExtendedPointY=OriginPoint.y + (OriginPoint.y - FarPoint1.y)
    ExtendedPoint=geometry.Point(ExtendedPointX, ExtendedPointY, srid=OriginPoint.srid)
    LineA=geometry.LineString(OriginPoint, ExtendedPoint, srid=OriginPoint.srid)
    LineB=geometry.LineString(OriginPoint, FarPoint2, srid=OriginPoint.srid)
    LineC=geometry.LineString(ExtendedPoint, FarPoint2, srid=OriginPoint.srid)
    a = LineA.length
    b = LineB.length
    c = LineC.length
    import math
    try:
        RadianAngle=math.acos((c*c-a*a-b*b)/(-2*a*b))
    except ValueError:
        return 'UNKNOWN'
    return math.degrees(RadianAngle)

def GetSpanAngleTable(station):
    """Calculates and returns a table of angles which includes the angle difference between every span that connect to a station"""
    if(not station):
        return ''
    Spans=station.GetNearbySpans()
    ResultRows=[]
    ResultRows.append('Spanid     Spanid     Angle')
    OriginPoint=station.Point
    for span in Spans:
        Span1FarPoint=span.FarStation(station).Point
        for span2 in Spans:
            if(span == span2):
                continue
            Span2FarPoint=span2.FarStation(station).Point
            Angle=CalculateAngle(OriginPoint, Span1FarPoint, Span2FarPoint)
            try:
                ResultRows.append('     '.join([str(span.id), str(span2.id), str(round(Angle,1))]))
            except TypeError:
                ResultRows.append('     '.join([str(span.id), str(span2.id), str(Angle)]))

    return '\n'.join(ResultRows)



def WalkDirectory(Root, Directories = [], Files = {}):
    """Root=the root directory that the walk starts on, or a subdirectory under the root that was recursively called
       Directories=An Empty List to Start, the full path of each directory under Root, including Root Itself
       Files=An Empty Dictionary to Start, the name of each file, with a full path.  The value of each file is the
             file last modify date"""
    for filename in os.listdir(Root):
        if(os.path.isdir(Root + filename)):
            WalkDirectory(Root + filename + '/', Directories, Files)
            Directories.append(Root + filename + '/')
        else:
            Files[Root + filename] = os.path.getmtime(Root + filename)
    return Directories, Files

def CreateDirectories(Directories):
    for directory in Directories:
        os.system("sudo -u apachescript sudo mkdir " + directory)
            

def GetRemoteFile(FileLocation, PickleIndicator=False):
    os.system("sudo -u apachescript sudo wget -O " + djangoproject.settings.COOPGISTemporaryFileName + 
              " http://" + djangoproject.settings.COOPGISServerIPAddress + '/' + FileLocation)
    File=open(djangoproject.settings.COOPGISTemporaryFileName, 'rb')
    if(PickleIndicator):
        try:
            Results=pickle.load(File)
        except:
            Results=''
    else:
        ResultsList=[]
        for char in File.read():
            ResultsList.append(char)
        Results=''.join(ResultsList)
    File.close()
    os.system("sudo -u apachescript sudo rm " + djangoproject.settings.COOPGISTemporaryFileName)
    return Results

